// compiler.cs

// CHAD (cheap and dirty) compiler interface to Phoenix

using System;
using System.Collections.Generic;

public class Compiler
{
    static void Main(string[] args)
    {
        Phx.Term.Mode termMode = Phx.Term.Mode.Normal;  // informa que o progrma terminou normalmente
        try
        {
            Compiler compiler = new Compiler(args);
        }
        catch (Exception exception)
        {
            Phx.Output.WriteLine("Unhandled Exception: {0}", exception);
            termMode = Phx.Term.Mode.Fatal;  // informa que o programa terminou com erro e abortou
        }

        Phx.Term.All(termMode);
    }

    Parser parser;

    Compiler(string[] args)
    {
        Intro(args);

        if (filename == null)
        {
            Console.WriteLine("usage: chadcompiler filename [-optimizeexpressions]");
            return;
        }
        Console.WriteLine("Compiling '{0}'", filename);

        parser = new Parser(this, filename);
        parser.Parse();

        foreach (Phx.Unit u in funcUnits)
        {
            ExecutePhases(u);
        }

        Outro();
    }
    //--------------------------------------------------------------------------

    Phx.ModuleUnit module;
    Phx.Targets.Runtimes.Runtime runtime;
    Phx.Targets.Architectures.Architecture arch;
    Phx.Lifetime lifetime;
    Phx.Types.Table typeTable;
    Phx.Symbols.Table moduleSymTable;
    Phx.Phases.PhaseConfiguration phaseConfig;

    Phx.Types.Type charType;
    Phx.Types.PointerType ptrToCharType;

    Phx.Symbols.FunctionSymbol printfSym;
    List<Phx.Unit> funcUnits = new List<Phx.Unit>();

    Phx.Symbols.GlobalVariableSymbol stringSym0;
    Phx.Symbols.GlobalVariableSymbol stringSym1;
    Phx.Symbols.GlobalVariableSymbol stringSym2;
    Phx.Symbols.GlobalVariableSymbol stringSym3;

    public Phx.Types.Type intType,otherType;

    uint externalIdCounter = 100;
    uint funcCounter = 1;

    string filename;

    Phx.Controls.SetBooleanControl optCtrl;

    void
    Intro
    (
       String[] argv      // command line arg strings
    )
    {
        // Do some initializations, and select the code generation
        // target.  Note that after this point we are largely
        // architecture neutral (that is, we don't know what processor
        // we're targeting...).

        runtime = Initialize(argv);
        arch = runtime.Architecture;

        // Build up a list of phases to apply to IR.

        phaseConfig = BuildPhaseList();

        // In this example we will make use of the global type table to
        // hold all types. This table is automatically created by
        // Phx.Initialize.BeginInitialization.

        typeTable = Phx.GlobalData.TypeTable;

        // We must create a ModuleUnit to represent the unit of
        // compilation. Before we can do this we need to create a module
        // lifetime and a name for the module.

        lifetime = Phx.Lifetime.New(Phx.LifetimeKind.Module, null);

        Phx.Name moduleName = Phx.Name.New(lifetime, "main.obj");

        module = Phx.ModuleUnit.New(lifetime, moduleName, null,
           typeTable, runtime.Architecture, runtime);

        // When Phoenix is processing there is a notion of a current
        // unit. The current unit is used to fill in important context in
        // certain apis. Creating a unit establishes it as the current unit.

        // Associated a symbol table with the module. This table will
        // hold both external symbols and module-scope symbols.

        moduleSymTable = Phx.Symbols.Table.New(module, 64, true);

        BeginScope(Phx.Symbols.NameMap.New(moduleSymTable, 64));	// global scope

        // Now build up symbols to represent the "compilation environment" --
        // namely, things referred to here but defined elsewhere.

        BuildExternalSymbols();

        // And a few more symbols and types for initialized data.

        BuildInitializedData();

        // The int type, in convenient public variable form:

        intType = typeTable.Int32Type;        
    }

    void Outro()
    {

        EndScope();

        // Prepare to write the object file.

        Phx.Coff.ObjectWriter objectWriter =
           Phx.Coff.ObjectWriter.New(lifetime, module, "main.obj", filename, arch, 0, 0, false);

        // Add a comment for the linker, telling it where to look for
        // the definitions for the external functions.

        objectWriter.AddComment(Phx.Coff.CommentType.Linker, "/DEFAULTLIB:LIBCMT");

        // Write the object.

        Phx.Output.Write("Writing main.obj..");
        objectWriter.Write();
        Phx.Output.WriteLine("..done");
    }


    //--------------------------------------------------------------------------
    //
    // Description:
    //
    //    Initializations.
    //
    // Remarks:
    //
    //    Performs framework initializations. Process command line and
    //    environment arguments. Turn on a few controls by default.
    //
    // Returns:
    //
    //    Runtime for which we'll generate code.
    //
    //--------------------------------------------------------------------------

    Phx.Targets.Runtimes.Runtime
    Initialize
    (
       String[] argv      // command line arg strings
    )
    {
        // We will generate X86 code.

        Phx.Targets.Architectures.Architecture arch =
            Phx.Targets.Architectures.X86.Architecture.New();
        Phx.Targets.Runtimes.Runtime runtime =
            Phx.Targets.Runtimes.Vccrt.Win32.X86.Runtime.New(arch);
        Phx.GlobalData.RegisterTargetArchitecture(arch);
        Phx.GlobalData.RegisterTargetRuntime(runtime);

        // Targets MSIL
        Phx.Targets.Architectures.Architecture msilArch = Phx.Targets.Architectures.Msil.Architecture.New();
        Phx.Targets.Runtimes.Runtime winMSILRuntime =
                 Phx.Targets.Runtimes.Vccrt.Win.Msil.Runtime.New(msilArch);

        Phx.GlobalData.RegisterTargetArchitecture(msilArch);
        Phx.GlobalData.RegisterTargetRuntime(winMSILRuntime);

        // Initialize the infrastructure.

        Phx.Initialize.BeginInitialization();
        Phx.Symbols.OrderExtensionObject.Register();

        // We want to supply a plain filename on the command line and not
        // have to prepend Phoenix control syntax (e.g. "-input:blah.txt");
        // to do this we create a default control that processes any
        // command-line arguments that Phoenix doesn't recognize as a control.

        FilenameCtrl filenameCtrl = new FilenameCtrl(this);
        Phx.Controls.Parser.RegisterDefaultControl(filenameCtrl);

        // Boolean control to enable/disable expression optimization

        optCtrl = Phx.Controls.SetBooleanControl.New("optimizeexpressions",
            "Enable expression optimization", "CHAD compiler");

        // Initialize controls set by command line, register plugins,
        // etc.

        Phx.Initialize.EndInitialization("PHX|*|_PHX_|", argv);

        // Enable some controls. We want to see the names of types, types
        // in our IR dumps, linenumbers in our IR dumps, and linenumbers
        // in our object file.

        Phx.Controls.Parser.ParseArgumentString(null,
           "-dumptypesym -dump:types -dump:linenumbers -lineleveldebug");

        return runtime;
    }

    class FilenameCtrl : Phx.Controls.DefaultControl
    {
        private Compiler compiler;
        public FilenameCtrl(Compiler compiler)
        {
            this.compiler = compiler;
        }
        public override void Process(string s)
        {
            compiler.filename = s;
        }
    }

    //--------------------------------------------------------------------------
    //
    // Description:
    //
    //    Build up a phase list for compilation.
    //
    // Returns:
    //
    //    Phase list to use in processing each func.
    //
    //--------------------------------------------------------------------------

    Phx.Phases.PhaseConfiguration
    BuildPhaseList()
    {
        // When we get around to generating IR, we'll need to gradually
        // transform it into x86 machine code. Phx provides a number of
        // phases to help in this task. The phases are strung together
        // in a list, which we'll now build.

        Phx.Phases.PhaseConfiguration config;
        Phx.Phases.PhaseList phaseList;

        config = Phx.Phases.PhaseConfiguration.New(Phx.GlobalData.GlobalLifetime,
           "Hello Phases");
        phaseList = config.PhaseList;

        phaseList.AppendPhase(Phx.Types.TypeCheckPhase.New(config));
        phaseList.AppendPhase(Phx.MirLowerPhase.New(config));
        phaseList.AppendPhase(Phx.Targets.Runtimes.CanonicalizePhase.New(config));
        phaseList.AppendPhase(Phx.Targets.Runtimes.LowerPhase.New(config));
        phaseList.AppendPhase(Phx.RegisterAllocator.LinearScan.RegisterAllocatorPhase.New(config));
        phaseList.AppendPhase(Phx.StackAllocatePhase.New(config));
        phaseList.AppendPhase(Phx.Targets.Runtimes.FrameGenerationPhase.New(config));
        phaseList.AppendPhase(Phx.Targets.Runtimes.SwitchLowerPhase.New(config));
        phaseList.AppendPhase(Phx.Graphs.BlockLayoutPhase.New(config));
        phaseList.AppendPhase(Phx.FlowOptimizer.FlowOptimizationsPhase.New(config));

        // These two are defined below.

        phaseList.AppendPhase(Encode.New(config));
        phaseList.AppendPhase(Lister.New(config));

        // Add in any target-specific phases (eg x87 register allocation).

        runtime.AddPhases(config);

        // Add in any plugin phases.

        Phx.GlobalData.BuildPlugInPhases(config);

        return config;
    }

    //--------------------------------------------------------------------------
    //
    // Description:
    //
    //    Phase for encoding ...
    //
    // Returns:
    //
    //    New encoding phase
    //
    //--------------------------------------------------------------------------

    class Encode : Phx.Phases.Phase
    {
        public static Encode
        New
        (
           Phx.Phases.PhaseConfiguration config
        )
        {
            Encode encode = new Encode();

            encode.Initialize(config, "Encoding");

#if (PHX_DEBUG_SUPPORT)

         encode.PhaseControl = Phx.Targets.Runtimes.Encode.DebugControl;

#endif
            return encode;
        }

        override protected unsafe void
        Execute
        (
           Phx.Unit unit
        )
        {
            Phx.FunctionUnit func = unit as Phx.FunctionUnit;
            Phx.Targets.Runtimes.Encode encode = func.Encode;
            Phx.Unit module = func.ParentUnit;
            Phx.Symbols.FunctionSymbol funcSym = func.FunctionSymbol;

            func.EncodedIRLifetime = module.Lifetime;
            uint binarySize = encode.FinalizeIR();

            Phx.IR.DataInstruction encodedInstr =
               Phx.IR.DataInstruction.New(module, binarySize);

            // The function is this big.

            funcSym.ByteSize = binarySize;

            // The call to GetDatPtr returns a pointer and so is unsafe.

            encode.Function(encodedInstr.GetDataPointer(0));

            // Copy fixups and debug.

            encodedInstr.FixupList = encode.FixupList;
            encodedInstr.DebugOffsets = encode.DebugOffsets;

            // Set the func sym's location to the encoded IR.

            funcSym.Location = Phx.Symbols.DataLocation.New(encodedInstr);

            // Add the encoded IR to the appropriate section.

            Phx.Symbols.SectionSymbol textSectSym =
               module.SymbolTable.ExternIdMap.Lookup(
                  (uint)Phx.Symbols.PredefinedCxxILId.Text) as Phx.Symbols.SectionSymbol;

            textSectSym.Section.AppendInstruction(encodedInstr);

            // And note that the function ended up here.

            funcSym.AllocationBaseSectionSymbol = textSectSym;
        }
    }

    //--------------------------------------------------------------------------
    //
    // Description:
    //
    //    Phase for producing an assembly listing.
    //
    // Returns:
    //
    //    New listing phase
    //
    //--------------------------------------------------------------------------

    class Lister : Phx.Phases.Phase
    {
        public static Lister
        New
        (
           Phx.Phases.PhaseConfiguration config
        )
        {
            Lister lister = new Lister();

            lister.Initialize(config, "Encoding");

#if (PHX_DEBUG_SUPPORT)

         lister.PhaseControl = Phx.Targets.Runtimes.Lister.DebugControl;

#endif
            return lister;
        }

        override protected void
        Execute
        (
           Phx.Unit unit
        )
        {
            Phx.FunctionUnit func = unit as Phx.FunctionUnit;

            func.Lister.Function();
        }
    }

    //--------------------------------------------------------------------------
    //
    // Description:
    //
    //    Creates symbols and types for externals -- things that are referred
    //    to within our program but defined elsewhere.
    //
    // Returns:
    //
    //    Nothing.
    //
    //--------------------------------------------------------------------------

    void
    BuildExternalSymbols()
    {
        // In this case the only apparent symbol is printf, with
        // signature
        //
        //    int __cdecl printf(const char *, ...);
        //
        // Here the __cdecl indicates the calling convention.
        //
        // Let's first tackle printf. To begin with, we must create a
        // type object that represents the function signature.  In
        // Phoenix, function types are created with via the
        // FunctionTypeBuilder:

        Phx.Types.FunctionTypeBuilder funcTypeBuilder =
           Phx.Types.FunctionTypeBuilder.New(typeTable);

        funcTypeBuilder.Begin();

        // Now indicate that this function requires the cdecl calling convention.

        funcTypeBuilder.CallingConventionKind = Phx.Types.CallingConventionKind.CDecl;

        // Set the return value type. Here it is int. We'll size
        // 'int' to be the register size on the target machine, and
        // use a type to match.

        funcTypeBuilder.AppendReturnType(module.RegisterIntType);

        // Set the first argument type. We disregard 'const' here since
        // it is largely a frontend notion. We'll have our compiler use
        // 8 bit characters. Also, we'll assume we have standard-sized
        // pointers.

        charType = typeTable.Character8Type;

        ptrToCharType = Phx.Types.PointerType.New(typeTable,
           Phx.Types.PointerTypeKind.UnmanagedPointer, typeTable.NativePointerBitSize,
           charType, null);

        // As an alternative, we could have used
        //
        //    typeTable.GetUnmanagedPointerType(charType);
        //
        // which is what we'll do in the future when we create pointer types.

        funcTypeBuilder.AppendArgumentType(ptrToCharType);

        // Now for the ... parameter. Strictly speaking this argument
        // doesn't have a type. In Phoenix we use FuncArgs to represent
        // special arguments like ... . Now, ... doesn't have a type,
        // but the method requires one, so we use the UnknownType.

        Phx.Types.FunctionArgument ellipsisArg = Phx.Types.FunctionArgument.New(typeTable,
           Phx.Types.FunctionArgumentKind.Ellipsis, typeTable.UnknownType);

        funcTypeBuilder.AppendArgumentFunctionArgument(ellipsisArg);

        // Now the funcType is ready to be extracted.

        Phx.Types.FunctionType printfType = funcTypeBuilder.GetFunctionType();

        // Now we're ready to create a symbol to represent printf itself. First
        // we must give it a linker name. To follow convention we prepend an
        // underscore. printf is external, so we indicate its visibility as a
        // reference.

        Phx.Name printfName = Phx.Name.New(lifetime, "_printf");

        printfSym = Phx.Symbols.FunctionSymbol.New(moduleSymTable, 0, printfName, printfType,
           Phx.Symbols.Visibility.GlobalReference);
    }

    Phx.FunctionUnit func;
    Phx.Lifetime funcLifetime;
    public Phx.Symbols.Table funcSymTable;

    public void
    BuildFunction
    (
        string fnname,
        Phx.Types.Type type,
        List<Parser.ParamInfo> paramList
    )
    {
        AssertUniqueInCurrentScope(fnname);

        if (paramList.Count > 4)
        {
            parser.UhOh("Too many parameters to " + fnname);
        }
        Phx.Types.Type retType = type;
        Phx.Types.Type[] paramTypes = new Phx.Types.Type[4];
        for (int i = 0; i < 4; ++i)
        {
            if (i < paramList.Count)
                paramTypes[i] = paramList[i].type;
            else
                paramTypes[i] = null;
        }

        Phx.Types.FunctionType fnType = typeTable.GetFunctionType(
            Phx.Types.CallingConventionKind.CDecl,
            retType,
            paramTypes[0],
            paramTypes[1],
            paramTypes[2],
            paramTypes[3]
        );

        Phx.Symbols.FunctionSymbol fnSym = Phx.Symbols.FunctionSymbol.New(
            moduleSymTable,
            0,
            Phx.Name.New(lifetime, fnname),
            fnType,
            Phx.Symbols.Visibility.GlobalDefinition);

        AssertUniqueInCurrentScope(fnname);
        AddSymbolToCurrentScope(fnSym);

        // BuildFnFunc0(fnSym)

        // First we'll need a lifetime just for this function.

        funcLifetime =
           Phx.Lifetime.New(Phx.LifetimeKind.Function, null);

        // Next we'll create the funcUnit. There are a lot of
        // parameters here.

        func = Phx.FunctionUnit.New(
            funcLifetime,
            fnSym,
            Phx.CodeGenerationMode.Native,
            typeTable,
            module.Architecture,
            module.Runtime,
            module,
            funcCounter++
        );

        // Associate some debugInfo, so we can track source positions.

        Phx.Debug.Info.New(lifetime, func);

        funcSymTable = Phx.Symbols.Table.New(func, 64, true);

        BeginScope();	// function scope (parameters go here)

        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);

        func.FirstInstruction.DebugTag = func.CurrentDebugTag;

        AddParametersToFuncSymbolTable(paramList);

        // Build ENTERFUNC

        Phx.IR.LabelInstruction enterInstr = Phx.IR.LabelInstruction.New(func,
          Phx.Common.Opcode.EnterFunction, fnSym);

        foreach (Parser.ParamInfo param in paramList)
        {
            Phx.Symbols.LocalVariableSymbol sym =
                (Phx.Symbols.LocalVariableSymbol)LookupSymbol(param.name);
            Phx.IR.VariableOperand opnd
                = Phx.IR.VariableOperand.New(
                    func,
                    sym.Type,
                    sym
                );
            enterInstr.AppendDestination(opnd);
        }
        func.FirstInstruction.InsertAfter(enterInstr);

        // There needs to be a edge from start to enter

        Phx.IR.LabelOperand labelOpnd = Phx.IR.LabelOperand.New(func, enterInstr);
        func.FirstInstruction.AppendLabelSource(Phx.IR.LabelOperandKind.Technical, labelOpnd);

        // create a final exit point for the function. So we do that first,
        // so any return instructions will have a destination.

        exitInstr = Phx.IR.LabelInstruction.New(func,
           Phx.Common.Opcode.ExitFunction);
    }

    Phx.IR.LabelInstruction exitInstr;

    public void
    BuildFuncEnd()
    {
        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);

        exitInstr.DebugTag = func.CurrentDebugTag;	// update exitInstr's debug tag.

        func.LastInstruction.InsertBefore(exitInstr);

        // Update source location on END.

        func.LastInstruction.DebugTag = func.CurrentDebugTag;

        // Initialize the profile info for the function.

        Phx.Profile.Info profileInfo = func.ProfileInfo;

        profileInfo.InitializeInfo();

        // Now that we're done with IR for this unit, pop it off the context
        // stack (creating the unit pushed it on).

        func.Context.PopUnit();

        // Add to the list of funcunits.
        funcUnits.Add(func);

        EndScope();
    }


    //--------------------------------------------------------------------------
    //
    // Description:
    //
    //    Create symbols and types for initialized data. Also create
    //    the initalizer data IR.
    //
    // Returns:
    //
    //    Nothing.
    //
    //--------------------------------------------------------------------------

    void
    BuildInitializedData()
    {
        // There are a few more symbols that we will need. The four
        // literal strings will need symbols.
        // Since these symbols represent
        // initialized global data values, it's probabaly time to begin
        // thinking about a representation for the initial values as
        // well.
        //
        // In Phx, initial values are represented by
        // DataInstrs. DataInstrs must live in suitable sections; and
        // which sections they appear in depends on what the data instrs
        // represent. In this case all our global data is read only, and
        // so they will appear in a section called .rdata (for read-only
        // data). And to represent the .rdata section we need yet
        // another symbol...
        //
        // It turns out there is "builtin" support for creating the
        // "well known" sections (later on we'll also need .text to hold
        // the code we create). We simply need to call target.ModuleInitialize
        // and the proper sections will be added to the module's sym
        // table. So let's do that now. (It's instructive to call
        // moduleSymTable.Dump() before and after this to see what shows up --
        // lots of exotic seeming sections like .xdata$x).

        runtime.ModuleInitialize(module);

        // However, there's no harm in creating our own section for
        // data; any of the builtin sections created by ModuleInitialize that
        // end up empty will be omitted from the final object file, and
        // the name of the section, while important, takes a backseat to
        // the section properties. So we'll create a new section for our
        // strings, and then put the vtables in the normal .rdata
        // section.

        Phx.Symbols.SectionSymbol stringSectSym = Phx.Symbols.SectionSymbol.New(moduleSymTable, 0,
           Phx.Name.New(lifetime, ".strings"));

        // We want this section to hold initialized, read only data. We
        // don't care about alignment, really, but will use 4 byte
        // minimum.

        stringSectSym.IsReadable = true;
        stringSectSym.IsInitialize = true;
        stringSectSym.Alignment = new Phx.Alignment(Phx.Alignment.Kind.AlignTo4Bytes);

        // Along with the sectSym we need a section. At this point we have
        // to divulge that we are creating output in COFF.

        Phx.Coff.Section stringSection =
           Phx.Coff.Section.New(stringSectSym);
        
        stringSectSym.Section = stringSection;

        // Now to create the initial values. Note that we have two

        // Now we need some symbols so we can refer to these strings in
        // our program IR. The names of these symbols are unimportant,
        // so we'll just make something up. The type of each symbol is
        // an array of characters with the appropriate length.

        stringSym0 =
           CreateInitializedString("\n", "$SG666", stringSectSym);
        stringSym1 =
           CreateInitializedString("%d\n", "$SG667", stringSectSym);
        stringSym2 =
           CreateInitializedString("%d\t%d\n", "$SG668", stringSectSym);
        stringSym3 =
           CreateInitializedString("%d\t%d\t%d\n", "$SG669", stringSectSym);
    }

    //--------------------------------------------------------------------------
    //
    // Description:
    //
    //    Create an initialized string.
    //
    // Returns:
    //
    //    Symbol representing the initialized string.
    //
    //--------------------------------------------------------------------------

    Phx.Symbols.GlobalVariableSymbol
    CreateInitializedString
    (
       String value,
       String symbolicName,
       Phx.Symbols.SectionSymbol stringSectSym
    )
    {
        uint len = (uint)value.Length + 1;

        Phx.Name stringName =
           Phx.Name.New(lifetime, symbolicName);

        Phx.Types.Type stringType =
           Phx.Types.UnmanagedArrayType.New(typeTable, Phx.Utility.BytesToBits(len),
              null, charType);

        Phx.Symbols.GlobalVariableSymbol stringSym =
           Phx.Symbols.GlobalVariableSymbol.New(moduleSymTable, externalIdCounter++,
              stringName, stringType, Phx.Symbols.Visibility.File);
        stringSym.AllocationBaseSectionSymbol = stringSectSym;
        stringSym.Alignment = new Phx.Alignment(Phx.Alignment.Kind.AlignTo1Byte);
        // stringSym.NeedsToBeEmitted = true;

        Phx.Section stringSection = stringSectSym.Section;

        Phx.IR.DataInstruction stringInstr =
           Phx.IR.DataInstruction.New(stringSection.DataUnit, len);

        stringInstr.WriteString(0, value);
        stringSection.AppendInstruction(stringInstr);
        stringSym.Location = Phx.Symbols.DataLocation.New(stringInstr);

        return stringSym;
    }

    //--------------------------------------------------------------------------
    //
    // Description:
    //
    //    Run the phases on a func unit.
    //
    // Returns:
    //
    //    Nothing.
    //
    //--------------------------------------------------------------------------

    void
    ExecutePhases
    (
       Phx.Unit funcUnit
    )
    {
        Phx.Threading.Context context = Phx.Threading.Context.GetCurrent();
        context.PushUnit(funcUnit);

        phaseConfig.PhaseList.DoPhaseList(funcUnit);

        context.PopUnit();
    }

    /////////////////////////////////////////////////////////////////////////////
    //	symbol table stuff
    //	incl. block scoping

    List<Phx.Symbols.NameMap> nameMapList = new List<Phx.Symbols.NameMap>();

    // top of stack (innermost scope) is index 0

    public void BeginScope()
    {
        nameMapList.Insert(0, Phx.Symbols.NameMap.New(funcSymTable, 64));
    }

    public void BeginScope(Phx.Symbols.NameMap nameMap)
    {
        nameMapList.Insert(0, nameMap);
    }

    public void EndScope()
    {
        nameMapList.RemoveAt(0);
    }

    Phx.Symbols.Symbol LookupSymbol(string name)
    // Look up name, starting in current scope and moving out to global scope
    {
        Phx.Name symName = Phx.Name.New(funcLifetime, name);
        foreach (Phx.Symbols.NameMap nameMap in nameMapList)
        {
            Phx.Symbols.Symbol s = nameMap.Lookup(symName);
            if (s != null)
                return s;
        }
        return null;
    }

    void AssertUniqueInCurrentScope(string name)
    {
        if (nameMapList[0].Lookup(Phx.Name.New(funcLifetime, name)) != null)
        {
            parser.UhOh("Redefining " + name);
        }
    }

    void AddSymbolToCurrentScope
    (
        Phx.Symbols.Symbol s
    )
    {
        nameMapList[0].Insert(s);
    }

    void AddToFuncSymbolTable
    (
        string name,
        Phx.Types.Type type,
        Phx.Symbols.StorageClass storageClass
    )
    {
        Phx.Symbols.LocalVariableSymbol sym
            = Phx.Symbols.LocalVariableSymbol.New(
                funcSymTable,
                0,
                Phx.Name.New(funcLifetime, name),
                type,
                storageClass
            );
        sym.Alignment = Phx.Alignment.NaturalAlignment(sym.Type);
        nameMapList[0].Insert(sym);
    }
    public void AddLocalToFuncSymbolTable(string name, Phx.Types.Type type)
    {
        AssertUniqueInCurrentScope(name);
        AddToFuncSymbolTable(name, type, Phx.Symbols.StorageClass.Auto);
    }
    public void AddParametersToFuncSymbolTable(List<Parser.ParamInfo> p)
    {
        foreach (Parser.ParamInfo param in p)
        {
            AddToFuncSymbolTable(param.name, param.type, Phx.Symbols.StorageClass.Parameter);
        }
    }

    public void AddGlobalVarToGlobalSymbolTable
    (
        string name,
        Phx.Types.Type type,
        int initialValue
    )
    {
        AssertUniqueInCurrentScope(name);
        Phx.Symbols.GlobalVariableSymbol sym
            = Phx.Symbols.GlobalVariableSymbol.New(
                moduleSymTable,
                externalIdCounter++,
                Phx.Name.New(lifetime, name),
                type,
                Phx.Symbols.Visibility.GlobalDefinition
            );
        AddSymbolToCurrentScope(sym);

        // Global variables live in the Data section:

        sym.AllocationBaseSectionSymbol = moduleSymTable.ExternIdMap.Lookup(
              (uint)Phx.Symbols.PredefinedCxxILId.Data) as Phx.Symbols.SectionSymbol;
        sym.Alignment = Phx.Alignment.NaturalAlignment(sym.Type);

        Phx.Section globalSection = sym.AllocationBaseSectionSymbol.Section;

        // Create an initializer instruction:

        Phx.IR.DataInstruction initializerInstr = Phx.IR.DataInstruction.New(
            globalSection.DataUnit, 4);
        initializerInstr.WriteInt32(0, initialValue);
        globalSection.AppendInstruction(initializerInstr);
        sym.Location = Phx.Symbols.DataLocation.New(initializerInstr);
    }

    Phx.Symbols.NonLocalVariableSymbol MakeProxyInFuncSymbolTable(Phx.Symbols.GlobalVariableSymbol global)
    {
        // Returns a NonLocalVariableSymbol proxy for global.
        // First, see if a proxy already exists in funcSymTable;
        // if it does, return it:
        Phx.Symbols.Symbol sym = funcSymTable.ExternIdMap.Lookup(global.ExternId);
        if (sym != null)
            return sym as Phx.Symbols.NonLocalVariableSymbol;

        // Otherwise, create a new NonLocalVariableSymbol.
        return Phx.Symbols.NonLocalVariableSymbol.New(funcSymTable, global);
    }

    public Phx.IR.Operand NewTemp(Phx.Types.Type type)
    {
        return Phx.IR.VariableOperand.NewExpressionTemporary(func, type);
    }
    //Manipulate a constante operator, only in val
    public Phx.IR.ImmediateOperand NewImmediateOperand(int val)
    {
        return Phx.IR.ImmediateOperand.New(
            func,
            typeTable.Int32Type,
            val
        );
    }

    public Phx.IR.LabelInstruction NewLabelInstr()
    {
        return Phx.IR.LabelInstruction.New(func);
    }

    public Phx.IR.VariableOperand MakeVarOpnd(string name)
    {
        Phx.Symbols.Symbol s = LookupSymbol(name);
        if (s == null)
            parser.UhOh("Unknown variable: " + name);
        if (s is Phx.Symbols.GlobalVariableSymbol)
        {
            // if it's a global variable, we have to make a proxy
            // (NonLocalVariableSymbol) in the funcunit symbol table.

            s = MakeProxyInFuncSymbolTable(s as Phx.Symbols.GlobalVariableSymbol);
        }
        return Phx.IR.VariableOperand.New(func, s.Type, s);
        /// Question: If it's a global variable, should we return a MemoryOperand here?
    }

    public Phx.IR.Operand EmitAssign(Phx.IR.Operand dst, string op, Phx.IR.Operand src)
    {
        Phx.Opcode opcode = Phx.Common.Opcode.Assign;

        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);

        Phx.IR.Instruction instr = Phx.IR.ValueInstruction.New(func, opcode);

        instr.AppendSource(src);
        instr.AppendDestination(dst);

        func.LastInstruction.InsertBefore(instr);

        return instr.DestinationOperand;
    }

    public Phx.IR.Operand EmitUnOp(string op, Phx.Types.Type type, Phx.IR.Operand src)
    {
        Phx.Opcode opcode = null;
        switch (op)
        {
            case "-": opcode = Phx.Common.Opcode.Negate; break;
            default: parser.UhOh("Unknown unop: " + op); break;
        }

        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);

        Phx.IR.Instruction instr = Phx.IR.ValueInstruction.NewUnaryExpression(func, opcode, type, src);

        func.LastInstruction.InsertBefore(instr);

        return instr.DestinationOperand;
    }

    public Phx.IR.Operand EmitBinOp(string op, Phx.Types.Type type, Phx.IR.Operand src1, Phx.IR.Operand src2)
    {
        Phx.Opcode opcode = null;
        switch (op)
        {
            case "+": opcode = Phx.Common.Opcode.Add; break;
            case "-": opcode = Phx.Common.Opcode.Subtract; break;
            case "*": opcode = Phx.Common.Opcode.Multiply; break;
            case "/": opcode = Phx.Common.Opcode.Divide; break;
            default: parser.UhOh("Unknown binop: " + op); break;
        }
        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);

        Phx.IR.Instruction instr = Phx.IR.ValueInstruction.NewBinaryExpression(func, opcode, type, src1, src2);

        func.LastInstruction.InsertBefore(instr);

        return instr.DestinationOperand;
    }

    public void OptimizeLastInstr()
    {
        if (optCtrl.GetValue(null))
        {
            Phx.Expression.Optimization.Optimize(func.LastInstruction.Previous,
                Phx.Expression.CompareControl.Expressions, Phx.Expression.OptimizationControl.Full);
        }
    }

    public void EmitCall(Phx.IR.Operand dstOpnd, string name, List<Phx.IR.Operand> args)
    {
        Phx.Symbols.FunctionSymbol myFnSym = (Phx.Symbols.FunctionSymbol)LookupSymbol(name);

        if (myFnSym == null)
            parser.UhOh("Unknown function: " + name);

        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);

        Phx.IR.Instruction callInstr
            = Phx.IR.Instruction.NewCall(
                func,
                Phx.Common.Opcode.Call,
                myFnSym
            );
        foreach (Phx.IR.Operand opnd in args)
        {
            callInstr.AppendSource(opnd);
        }
        callInstr.AppendDestination(dstOpnd);

        func.LastInstruction.InsertBefore(callInstr);
    }

    public void EmitLabel(Phx.IR.LabelInstruction labelInstr)
    {
        func.LastInstruction.InsertBefore(labelInstr);
    }

    public void EmitGoto(Phx.IR.LabelInstruction labelInstr)
    {
        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);
        Phx.IR.BranchInstruction gotoInstr = Phx.IR.BranchInstruction.New(
            func,
            Phx.Common.Opcode.Goto,
            labelInstr);

        func.LastInstruction.InsertBefore(gotoInstr);
    }

    public void EmitCbranch
    (
        Phx.IR.Operand left,
        string relop,
    Phx.IR.Operand right,
    Phx.IR.LabelInstruction trueLabelInstr,
    Phx.IR.LabelInstruction falseLabelInstr
    )
    {
        Phx.ConditionCode condCode = 0;
        switch (relop)
        {
            case "==": condCode = Phx.ConditionCode.EQ; break;
            case "!=": condCode = Phx.ConditionCode.NE; break;
            case "<": condCode = Phx.ConditionCode.LT; break;
            case "<=": condCode = Phx.ConditionCode.LE; break;
            case ">": condCode = Phx.ConditionCode.GT; break;
            case ">=": condCode = Phx.ConditionCode.GE; break;
            default: parser.UhOh(relop + " is not a relOp"); break;
        }

        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);
        Phx.IR.Instruction cmpInstr
            = Phx.IR.Instruction.NewCompareTemporary(
                func,
                Phx.Common.Opcode.Compare,
                (int)condCode,
                typeTable.ConditionType,
                left,
                right
            );
        func.LastInstruction.InsertBefore(cmpInstr);

        Phx.IR.Instruction cbranchInstr
            = Phx.IR.Instruction.NewBranch(
                func,
                Phx.Common.Opcode.ConditionalBranch,
                (int)Phx.ConditionCode.True,
                cmpInstr.DestinationOperand,
                trueLabelInstr,
                falseLabelInstr
            );
        func.LastInstruction.InsertBefore(cbranchInstr);
    }

    public void EmitReturn(Phx.IR.Operand e)
    {
        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);

        Phx.IR.Instruction retInstr = Phx.IR.BranchInstruction.NewReturn(func,
           Phx.Common.Opcode.Return, exitInstr);

        retInstr.AppendSource(e);

        func.LastInstruction.InsertBefore(retInstr);
    }

    public void EmitPrint(List<Phx.IR.Operand> args)
    {
        func.CurrentDebugTag = func.DebugInfo.CreateTag(
            Phx.Name.New(lifetime, filename), parser.scanner.lineno, 0, 0);

        Phx.IR.Instruction callInstr = Phx.IR.Instruction.NewCall(
            func,
            Phx.Common.Opcode.Call,
            printfSym);
        Phx.Symbols.GlobalVariableSymbol stringSym = null;
        switch (args.Count)
        {
            case 0: stringSym = stringSym0; break;
            case 1: stringSym = stringSym1; break;
            case 2: stringSym = stringSym2; break;
            case 3: stringSym = stringSym3; break;
            default: parser.UhOh("Print: Too many args"); break;
        }

        Phx.Symbols.NonLocalVariableSymbol localStringSym =
            MakeProxyInFuncSymbolTable(stringSym);

        Phx.IR.VariableOperand stringOpnd = Phx.IR.VariableOperand.New(
            func,
            charType,
            localStringSym
        );
        stringOpnd.ChangeToAddress();

        callInstr.AppendSource(stringOpnd);
        foreach (Phx.IR.Operand o in args)
            callInstr.AppendSource(o);

        func.LastInstruction.InsertBefore(callInstr);
    }
}